// more flags for sound volume control, version 2
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alsa/asoundlib.h>

typedef struct {
    long min;
    long max;
    long current;
    int is_capture;  // 1 = capture (mic), 0 = playback (speakers)
} VolumeInfo;

// List all channels with type
int list_channels() {
    snd_mixer_t *mixer;
    snd_mixer_elem_t *elem;
    const char *card = "default";
    int err;

    if ((err = snd_mixer_open(&mixer, 0)) < 0) {
        fprintf(stderr, "Open mixer failed: %s\n", snd_strerror(err));
        return err;
    }

    if ((err = snd_mixer_attach(mixer, card)) < 0) {
        fprintf(stderr, "Attach to card failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
        fprintf(stderr, "Register elements failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    if ((err = snd_mixer_load(mixer)) < 0) {
        fprintf(stderr, "Load elements failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    printf("Available audio channels:\n");
    for (elem = snd_mixer_first_elem(mixer); elem != NULL; elem = snd_mixer_elem_next(elem)) {
        if (snd_mixer_selem_is_active(elem)) {
            const char *name = snd_mixer_selem_get_name(elem);
            int is_capture = snd_mixer_selem_has_capture_volume(elem);
            printf("  - %s (%s)\n", name, is_capture ? "capture" : "playback");
        }
    }

    snd_mixer_close(mixer);
    return 0;
}

// Get volume info (works for both types)
int get_volume_info(VolumeInfo *info, const char *selem_name) {
    snd_mixer_t *mixer;
    snd_mixer_selem_id_t *sid;
    const char *card = "default";
    int err;

    if ((err = snd_mixer_open(&mixer, 0)) < 0) {
        fprintf(stderr, "Open mixer failed: %s\n", snd_strerror(err));
        return err;
    }

    if ((err = snd_mixer_attach(mixer, card)) < 0) {
        fprintf(stderr, "Attach to card failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    if ((err = snd_mixer_selem_register(mixer, NULL, NULL)) < 0) {
        fprintf(stderr, "Register elements failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    if ((err = snd_mixer_load(mixer)) < 0) {
        fprintf(stderr, "Load elements failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    snd_mixer_selem_id_alloca(&sid);
    snd_mixer_selem_id_set_name(sid, selem_name);
    snd_mixer_elem_t *elem = snd_mixer_find_selem(mixer, sid);
    if (!elem) {
        fprintf(stderr, "Error: Channel '%s' not found. Use 'list' to see available channels.\n", selem_name);
        snd_mixer_close(mixer);
        return -1;
    }

    info->is_capture = snd_mixer_selem_has_capture_volume(elem);

    // Get volume range
    if (info->is_capture) {
        err = snd_mixer_selem_get_capture_volume_range(elem, &info->min, &info->max);
    } else {
        err = snd_mixer_selem_get_playback_volume_range(elem, &info->min, &info->max);
    }
    if (err < 0) {
        fprintf(stderr, "Get volume range failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    // Get current volume
    if (info->is_capture) {
        err = snd_mixer_selem_get_capture_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &info->current);
    } else {
        err = snd_mixer_selem_get_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, &info->current);
    }
    if (err < 0) {
        fprintf(stderr, "Get current volume failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    snd_mixer_close(mixer);
    return 0;
}

// Set volume (works for both types)
int set_volume_percent(int percent, const char *selem_name) {
    VolumeInfo info;
    if (get_volume_info(&info, selem_name) != 0) {
        return -1;
    }

    int clamped = percent < 0 ? 0 : (percent > 100 ? 100 : percent);
    long range = info.max - info.min;
    long new_vol = info.min + (range * clamped) / 100;

    snd_mixer_t *mixer;
    snd_mixer_selem_id_t *sid;
    const char *card = "default";
    int err;

    if ((err = snd_mixer_open(&mixer, 0)) < 0) {
        fprintf(stderr, "Open mixer failed: %s\n", snd_strerror(err));
        return err;
    }

    if ((err = snd_mixer_attach(mixer, card)) < 0) {
        fprintf(stderr, "Attach to card failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    snd_mixer_selem_register(mixer, NULL, NULL);
    snd_mixer_load(mixer);

    snd_mixer_selem_id_alloca(&sid);
    snd_mixer_selem_id_set_name(sid, selem_name);
    snd_mixer_elem_t *elem = snd_mixer_find_selem(mixer, sid);
    if (!elem) {
        fprintf(stderr, "Channel not found: %s\n", selem_name);
        snd_mixer_close(mixer);
        return -1;
    }

    // Apply volume
    if (info.is_capture) {
        err = snd_mixer_selem_set_capture_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, new_vol);
        err |= snd_mixer_selem_set_capture_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, new_vol);
    } else {
        err = snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_LEFT, new_vol);
        err |= snd_mixer_selem_set_playback_volume(elem, SND_MIXER_SCHN_FRONT_RIGHT, new_vol);
    }
    if (err < 0) {
        fprintf(stderr, "Set volume failed: %s\n", snd_strerror(err));
        snd_mixer_close(mixer);
        return err;
    }

    snd_mixer_close(mixer);
    return 0;
}

// Show help with option hints
void print_help(const char *prog_name) {
    printf("\n%s is a program for up or down your sound volume\n", prog_name);
    printf("\nUsage: %s [options] <command> [options/arguments]\n", prog_name);
    printf("Commands:\n");
    printf("  up                 Increase volume (default step: 5%%)\n");
    printf("  down               Decrease volume (default step: 5%%)\n");
    printf("  set <N>            Set volume to N%% (0-100)\n");
    printf("  show               Show current volume\n");
    printf("  list               List all channels (playback/capture)\n");
    printf("Options (any position):\n");
    printf("  --step <N>         Step size (1-100, for up/down)\n");
    printf("  --channel <name>   Target channel (default: Master)\n");
    printf("Examples:\n");
    printf("  %s up --channel Capture --step 3\n", prog_name);
    printf("  %s --step 10 down --channel Master\n", prog_name);
    printf("  %s set 88 --channel Capture\n", prog_name);
    printf("  %s --channel Capture show\n", prog_name);
}

int main(int argc, char *argv[]) {
    int step = 5;
    const char *selem_name = "Master";
    const char *command = NULL;
    int target_percent = -1;
    int command_found = 0;

    // First pass: extract options (--step, --channel) and find command
    for (int i = 1; i < argc; i++) {
        if (strcmp(argv[i], "--step") == 0 && i + 1 < argc) {
            step = atoi(argv[++i]);
            if (step < 1 || step > 100) {
                fprintf(stderr, "Error: Step must be 1-100\n");
                return 1;
            }
        } else if (strcmp(argv[i], "--channel") == 0 && i + 1 < argc) {
            selem_name = argv[++i];
        } else if (!command_found) {
            // Check if this is a valid command
            if (strcmp(argv[i], "up") == 0 || strcmp(argv[i], "down") == 0 ||
                strcmp(argv[i], "show") == 0 || strcmp(argv[i], "list") == 0) {
                command = argv[i];
                command_found = 1;
            } else if (strcmp(argv[i], "set") == 0) {
                command = argv[i];
                command_found = 1;
                // Find the percentage for "set" (next non-option argument)
                for (int j = i + 1; j < argc; j++) {
                    if (argv[j][0] != '-') {  // Skip other options
                        target_percent = atoi(argv[j]);
                        if (target_percent < 0 || target_percent > 100) {
                            fprintf(stderr, "Error: 'set' requires 0-100\n");
                            return 1;
                        }
                        break;
                    }
                }
                if (target_percent == -1) {
                    fprintf(stderr, "Error: 'set' needs a percentage (e.g., set 50)\n");
                    return 1;
                }
            } else {
                fprintf(stderr, "Error: Invalid command or option '%s'\n", argv[i]);
                fprintf(stderr, "Did you mean '--step'?\n");  // Hint for common typo
                print_help(argv[0]);
                return 1;
            }
        }
    }

    // Ensure command was found
    if (!command_found) {
        fprintf(stderr, "Error: No command provided\n");
        print_help(argv[0]);
        return 1;
    }

    // Execute command
    if (strcmp(command, "list") == 0) {
        return list_channels();
    }

    VolumeInfo info;
    if (get_volume_info(&info, selem_name) != 0) {
        return 1;
    }
    int current_percent = 100 * (info.current - info.min) / (info.max - info.min);

    if (strcmp(command, "show") == 0) {
        printf("Current volume (channel: %s, type: %s): %d%% (Max: 100%%)\n",
               selem_name, info.is_capture ? "capture" : "playback", current_percent);
    } else if (strcmp(command, "up") == 0) {
        int new_percent = current_percent + step;
        int ret = set_volume_percent(new_percent, selem_name);
        if (ret == 0) {
            printf("Volume (channel: %s) increased to: %d%%\n",
                   selem_name, new_percent > 100 ? 100 : new_percent);
        }
        return ret;
    } else if (strcmp(command, "down") == 0) {
        int new_percent = current_percent - step;
        int ret = set_volume_percent(new_percent, selem_name);
        if (ret == 0) {
            printf("Volume (channel: %s) decreased to: %d%%\n",
                   selem_name, new_percent < 0 ? 0 : new_percent);
        }
        return ret;
    } else if (strcmp(command, "set") == 0) {
        int ret = set_volume_percent(target_percent, selem_name);
        if (ret == 0) {
            printf("Volume (channel: %s) set to: %d%%\n", selem_name, target_percent);
        }
        return ret;
    }

    return 0;
}
